package exporters

import (
	"github.com/go-kit/log"
	"github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders"
	"github.com/prometheus/client_golang/prometheus"
)

type PlacementExporter struct {
	BaseOpenStackExporter
}

var defaultPlacementMetrics = []Metric{
	{Name: "resource_total", Fn: ListPlacementResourceProviders, Labels: []string{"hostname", "resourcetype"}},
	{Name: "resource_allocation_ratio", Labels: []string{"hostname", "resourcetype"}},
	{Name: "resource_reserved", Labels: []string{"hostname", "resourcetype"}},
	{Name: "resource_usage", Labels: []string{"hostname", "resourcetype"}},
}

func NewPlacementExporter(config *ExporterConfig, logger log.Logger) (*PlacementExporter, error) {
	exporter := PlacementExporter{
		BaseOpenStackExporter{
			Name:           "placement",
			ExporterConfig: *config,
			logger:         logger,
		},
	}
	for _, metric := range defaultPlacementMetrics {
		if exporter.isDeprecatedMetric(&metric) {
			continue
		}
		if !exporter.isSlowMetric(&metric) {
			exporter.AddMetric(metric.Name, metric.Fn, metric.Labels, metric.DeprecatedVersion, nil)
		}
	}
	return &exporter, nil
}

var allResourceProvidersErr error
var inventoryResultList = make(map[string]*resourceproviders.ResourceProviderInventories)
var usagesResultList = make(map[string]*resourceproviders.ResourceProviderUsage)

func PlacementTimedTask(exporter *BaseOpenStackExporter) {
	var allResourceProviders []resourceproviders.ResourceProvider
	if exporter.MetricIsEnabled("resource_total") || exporter.MetricIsEnabled("resource_allocation_ratio") || exporter.MetricIsEnabled("resource_reserved") || exporter.MetricIsEnabled("resource_usage") {
		allPagesResourceProviders, err := resourceproviders.List(exporter.Client, resourceproviders.ListOpts{}).AllPages()
		allResourceProvidersErr = err
		if err == nil {
			allResourceProviders, allResourceProvidersErr = resourceproviders.ExtractResourceProviders(allPagesResourceProviders)
			for _, resourceprovider := range allResourceProviders {
				inventoryResult, err := resourceproviders.GetInventories(exporter.Client, resourceprovider.UUID).Extract()
				allResourceProvidersErr = err
				if err == nil {
					inventoryResultList[resourceprovider.Name] = inventoryResult
					usagesResult, err := resourceproviders.GetUsages(exporter.Client, resourceprovider.UUID).Extract()
					allResourceProvidersErr = err
					if err == nil {
						usagesResultList[resourceprovider.Name] = usagesResult
					}
				}
			}
		}

	}
}

func ListPlacementResourceProviders(exporter *BaseOpenStackExporter, ch chan<- prometheus.Metric) error {

	for name, inventoryResult := range inventoryResultList {
		for k, v := range inventoryResult.Inventories {

			ch <- prometheus.MustNewConstMetric(exporter.Metrics["resource_total"].Metric,
				prometheus.GaugeValue, float64(v.Total), name, k)

			if m, ok := exporter.Metrics["resource_allocation_ratio"]; ok {
				ch <- prometheus.MustNewConstMetric(m.Metric,
					prometheus.GaugeValue, float64(v.AllocationRatio), name, k)
			}
			if m, ok := exporter.Metrics["resource_reserved"]; ok {
				ch <- prometheus.MustNewConstMetric(m.Metric,
					prometheus.GaugeValue, float64(v.Reserved), name, k)
			}
		}
	}

	if m, ok := exporter.Metrics["resource_usage"]; ok {
		for name, usagesResult := range usagesResultList {
			for k, v := range usagesResult.Usages {
				ch <- prometheus.MustNewConstMetric(m.Metric,
					prometheus.GaugeValue, float64(v), name, k)
			}
		}
	}

	return allResourceProvidersErr

}
